# Create your views here.
import json
import re
import urllib
from urllib import parse

from django.db.models import Q
from django.http import HttpResponse, JsonResponse
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt

from mockerAPP.Cache import *
from ProxyMocker.settings import logger


@method_decorator(csrf_exempt)
def home(request):
    # 参数获取
    logger.info("\n\n\n\n\n接收到新请求----------------------------------------------------------------------》\n")
    raw_request_data = request_params(request)
    body = raw_request_data.get("body")
    # 打印请求参数
    logger.debug("客户端请求参数处理结果：\n{}".format(json.dumps(raw_request_data, indent=4)))
    # mocker 过滤器
    code, result = mock_filter(user_ip=raw_request_data.get("ip"), api_path=raw_request_data.get("api"), querystring=raw_request_data.get("querystring"), params=body)
    # ip 不匹配，直接拦截
    if code == "forbidden":
        return HttpResponse(result)
    # ip,api_path,参数 都匹配，进行mock
    elif code == "mock":
        logger.info("mock 响应格式：{}".format(type(result)))
        # 插入record记录
        Record.objects.create(
            method=raw_request_data.get("method"),
            url=raw_request_data.get("api"),
            params=body,
            resp_type="mock",
            content=result
        )
        if isinstance(result, dict):
            return JsonResponse(result)
        else:
            return HttpResponse(result)
    # 转发到目标服务
    else:
        target_url = parse.urljoin(result, raw_request_data.get("full_path"))
        resp_text, resp_content_type = send_redirect(raw_request_data.get("method"), target_url, body, raw_request_data.get("headers"))
        # 插入record记录
        # if "application" in resp_content_type or "text/html" in resp_content_type:
        #     Record.objects.create(
        #         method=raw_request_data.get("method"),
        #         url=target_url,
        #         params=body,
        #         resp_type="redirect",
        #         content_type=resp_content_type,
        #         content=resp_text
        #     )
        return HttpResponse(resp_text, content_type=resp_content_type)


def request_params(request):
    """ request请求入参处理 """
    # 获取content_type
    content_type = request.content_type
    # headers 处理
    regex = re.compile('^HTTP_')
    headers = dict((regex.sub('', header), value) for (header, value)
                   in request.META.items() if header.startswith('HTTP_'))
    headers["Content-Type"] = content_type
    if "HOST" in headers: del headers["HOST"]
    # body 处理
    if content_type in ('multipart/form-data', 'application/x-www-form-urlencoded'):
        body = request.POST
    else:
        body = request.body.decode('UTF-8')
    # 客户端ip处理
    if 'HTTP_X_FORWARDED_FOR' in request.META:
        ip = request.META['HTTP_X_FORWARDED_FOR']
    else:
        ip = request.META['REMOTE_ADDR']
    params = {
        "api": request.path,
        "host": request.get_host(),
        "method": request.method,
        "meta": {},  # request.META,
        "querystring": request.GET.urlencode(),
        "full_path": request.get_full_path(),
        "content_type": request.content_type,
        "headers": headers,
        "body": body,
        "ip": ip
    }
    return params


def mock_filter(user_ip, api_path, querystring, params):
    """ mock 处理
    :return:
        # ip 不匹配，直接拦截。 return ("forbidden",msg)
        # ip匹配-》(api_path and filter) 匹配，则直接mock return（"mock",mock_resp）
        # ip匹配-》(api_path and filter) 不匹配，则进行转发("redirect",target_host)
    """

    fish_list_ip = Fish.objects.filter(Q(user_ip=user_ip) | Q(user_ip="*")).order_by("-id")
    logger.info("ip={ip} 匹配fish记录条目:{count}".format(ip=user_ip, count=len(fish_list_ip)))
    if len(fish_list_ip) == 0:  # ip 不匹配
        logger.warning("ip拦截处理:{}".format(user_ip))
        return ("forbidden", "<h1>User ip not match,You have been forbidden!</h1>")  # 返回禁止页
    # ip 匹配，查找api匹配记录
    fish_obj = fish_list_ip[0]  # 先默认已最新ip匹配记录为目标对象
    fish_list_api = fish_list_ip.filter(api_path=api_path).order_by("-id")
    logger.info("api_path={api_path} 匹配fish记录条目:{count}".format(api_path=api_path, count=len(fish_list_api)))

    if len(fish_list_api) > 0:  # api 匹配
        fish_obj = fish_list_api[0]  # 如果匹配多条记录，取第一条为目标对象
        pre_pycode = fish_obj.pre_pycode
        # logger.info("获取pre_pycode:{}".format(pre_pycode))
        querystring = urllib.parse.unquote(querystring, encoding='utf-8', errors='replace')
        try:
            # 将params 转成字典
            if not isinstance(params, dict):
                params = json.loads(params)
            # 将querystring解码
        except:
            logger.warning("请注意，params 无法转换成字典格式，在pre_pycode 和 mock_pycode 中无法直接用字典形式提取参数")
        logger.info("type(querystring):{},querystring:{}将传入到 precode 中进行处理".format(type(querystring), querystring))
        logger.info("type(params):{},params:{}将传入到 precode 中进行处理".format(type(params), params))
        pre = False
        try:
            pre = load_str_module(fish_obj.pre_pycode).param_filter(querystring, params)
        except Exception as e:
            logger.warning("pre_pycode 执行报错，请注意检查代码:{}".format(pre_pycode))
            logger.error(str(e))
        logger.info("pre_pycode 执行结果:{}".format(pre))
        if pre:  # 参数过滤匹配
            mock_pycode = fish_obj.mock_pycode
            logger.warning("mocker处理--------------------------------------------------》")
            # logger.info("获取mock_pycode:{}".format(mock_pycode))
            mock_resp = {}
            try:
                mock_resp = load_str_module(mock_pycode).mock_resp(querystring, params)
            except Exception as e:
                logger.warning("mock_pycode 执行报错，请注意检查代码:{}".format(mock_pycode))
                logger.error(str(e))
            logger.info("mock_pycode 执行结果:{}".format(mock_resp))
            return ("mock", mock_resp)
    logger.warning("转发处理HOST:-》{target_host}".format(target_host=fish_obj.target_host))
    return ("redirect", fish_obj.target_host)


def load_str_module(pycode):
    """加载字符串代码"""
    import types
    mymodule = types.ModuleType('mymodule')
    exec(pycode, mymodule.__dict__)
    return mymodule


def send_redirect(method, target_url, body, headers={}):
    """ 请求转发 """
    logger.info("begin {} {} \n body={},\n headers={}".format(method, target_url, body, headers))
    if "Content-Type" in headers:headers.pop("Content-Type")
    if str(method).lower() == "get":
        resp = requests.get(url=target_url)
    elif str(method).lower() == "post":
        resp = requests.post(url=target_url, data=body, headers=headers)
    elif str(method).lower() == "put":
        resp = requests.put(url=target_url, data=body, headers=headers)
    else:
        logger.error("{} not define yet!")
    resp_content_type = resp.headers.get("Content-Type")
    logger.info("target server response content_type:{}".format(resp_content_type))
    logger.info("target server response:{}".format(resp.text))
    return resp.text, resp_content_type


